#include "ajax_handler.h"
#include "settings/system_settings.h"
#include "web_handler.h"
#include <json/json.h>
#include <string>
#include <tsl/htrie_map.h>
#include <utils/custom.h>
#include <utils/logger.h>
/* clang-format */
#include <goahead.h>

#undef min

using WebsNode = tsl::htrie_map<char, std::function<Json::Value(Webs *, const std::string &)>>;

static const WebsNode AJAX_WEB_NODES = {
    {"login",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            std::string token = utils::get_rand(32);
            Json::Value param;
            Json::Reader jsonReader;

            HTELINK_LOG_DEBUG("input buf = %s", wp->input.buf);
            if (!jsonReader.parse(wp->input.buf, param, false)) {
                HTELINK_LOG_ERR("login parse buf error, buf = %s", (char *)wp->input.buf);
                return Json::nullValue;
            }
            HTELINK_LOG_DEBUG("param = %s", param.toStyledString().c_str());
            const std::string name = param["username"].asString();
            const std::string passwd = param["password"].asString();

            Json::Value result;
            result["code"] = 200;
            result["message"] = "验证成功";
            Json::Value tokenJson;
            tokenJson["token"] = token;
            result["data"] = tokenJson;
            return result;
        }                 },
    {"logout",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"UID",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"download",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"getBaseConfig",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            result["data"] = webconfig::GetJsonDataCenter();
            result["code"] = 200;
            result["message"] = "SUCCESS";
            return result;
        }                 },
    {"getGatewayInfo",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            result["data"] = webconfig::GetJsonGatewayInfo();
            result["code"] = 200;
            result["message"] = "SUCCESS";
            return result;
        }                 },
    {"getGatewayStatus",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            Json::Value statJson;
            statJson["status"] = true;
            result["code"] = 200;
            result["message"] = u"在线";
            result["data"] = statJson;
            return result;
        }                 },
    {"getDarcyConfig",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"reset",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"setNetworkConfig",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"restart",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"reboot",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"deviceType",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"commu",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"deviceList",
     [](Webs *wp,                     const std::string &subID) -> Json::Value {
            Json::Value result;
            return result;
        }                 },
    {"ctrlParams",       [](Webs *wp, const std::string &subID) -> Json::Value {
         Json::Value result;
         return result;
     }}
};

/* clang-format off */
static const WebsNode UID_WEB_NODES = {
    {"loadChildNodes/dataCenter", [](Webs *wp,const std::string &subID) -> Json::Value {
        Json::Value result;
        settings::ConfigParser configParser;
        std::vector<settings::ConfigParser::DataCenter> dataCenters = configParser.GetDataCenter();
        int index = 0;
        for (settings::ConfigParser::DataCenter &dc : dataCenters){
            char key[64];
            sprintf(key, "dataCenter/%d", index++);
            result[key] = dc.name;
        }
        return result;
    }},
    {"loadChildNodes/communicator", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    {"loadChildNodes/d", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    {"loadNode/getGatewayInfo", [](Webs *wp, const std::string &subID) -> Json::Value {
        settings::SystemSettings sysSettings;
        sysSettings.UpdateSysInfo();
        char disk[20], mem[20];
        sprintf(disk, "%d M/%d M", sysSettings.freeDiskSpace, sysSettings.totalDiskSpace);
        sprintf(mem, "%d M/%d M", sysSettings.freeMemory, sysSettings.totalMemory);
        Json::Value result;
        Json::Value diskJson;
        diskJson["free"] = sysSettings.freeDiskSpace;
        diskJson["total"] = sysSettings.totalDiskSpace;
        result["disk"] = diskJson;

        Json::Value memJson;
        memJson["free"] = sysSettings.freeMemory;
        memJson["total"] = sysSettings.totalMemory;
        result["memory"] = memJson;

        sysSettings.UpdateNetDev();
        Json::Value netJson;
        for (settings::SystemSettings::NetCard &netCard: sysSettings.netCards) {
            Json::Value netJ;
            netJ["ip"] = netCard.ipAddr;
            netJ["mac"] = netCard.MAC;
            netJson.append(netJ);
        }
        result["network"] = netJson;

        Json::Value sysJson;
        sysJson["version"] = sysSettings.appVer;
        sysJson["time"] = sysSettings.sysTime;
        sysJson["upgradeTime"] = sysSettings.upgradeTime;
        sysJson["updateConfigTime"] = sysSettings.updateConfigTime;
        result["system"] = sysJson;
        Json::Value root;
        root["data"]  =result;
        return root;
    }},
    {"loadNode/getGatewayStatus", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        Json::Value statJson;
        statJson["status"] = false;
        result["code"] = 200;
        result["message"] = "";
        result["data"] = statJson;
        return result;
    }},
    {"loadNode/dataCenter", [](Webs *wp, const std::string &subID) -> Json::Value {
        settings::ConfigParser configParser;
        std::vector<settings::ConfigParser::DataCenter> dataCenters = configParser.GetDataCenter();
        Json::Value result;
        for (settings::ConfigParser::DataCenter &center : dataCenters) {
            Json::Value centerJson;
            centerJson["id"] = center.ID;
            centerJson["type"] = center.protoType;
            centerJson["ip"] = center.host;
            centerJson["port"] = center.port;
            centerJson["isOnline"] = false;
            centerJson["recv"] = 0;

        }
        return result;
    }},
    {"loadNode/communicator", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    {"loadNode/d", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;}},
    {"loadNode/param", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    {"ctrl", [](Webs *wp, const std::string &subID) -> Json::Value {
        const char* id = websGetVar(wp, "deviceId", "");
        const char* name = websGetVar(wp, "paramName", "");
        const char* value = websGetVar(wp, "paramValue", "");
        return Json::nullValue;
    }},
    {"acquire", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    {"point", [](Webs *wp, const std::string &subID) -> Json::Value {
        Json::Value result;
        return result;
    }},
    };
/* clang-format on */

WebHandler::WebHandler()
{
    // WebNode webNode = {"loadChildNodes", {{"dataCenter", {}}, {"communicator", {}}, {"d", {}}}};
}

WebHandler *WebHandler::instance()
{
    static WebHandler *webHandler = nullptr;
    if (webHandler == nullptr) {
        webHandler = new WebHandler();
    }
    ASSERT(webHandler != nullptr);
    return webHandler;
}

bool WebHandler::LoginVerify(Webs *wp)
{
    char passbuf[ME_GOAHEAD_LIMIT_PASSWORD * 3 + 3];
    bool success;

    assert(wp);
    if (!wp->user && (wp->user = websLookupUser(wp->username)) == 0) {
        trace(5, "verifyUser: Unknown user \"%s\"", wp->username);
        return false;
    }
    /*
        Verify the password. If using Digest auth, we compare the digest of the password.
        Otherwise we encode the plain-text password and compare that
     */

    if (!wp->encoded) {
        fmt(passbuf, sizeof(passbuf), "%s:%s:%s", wp->username, "htelink", wp->password);
        wfree(wp->password);
        wp->password = websMD5(passbuf);
        wp->encoded = 1;
    }

    HTELINK_LOG_DEBUG("encode = %d, username = %s, password = %s, digest = %s, store password = %s", wp->encoded,
        wp->username, wp->password, wp->digest, wp->user->password);
    if (wp->digest) {
        success = smatch(wp->password, wp->digest);
    } else {
        success = smatch(wp->password, wp->user->password);
    }
    if (success) {
        trace(5, "User \"%s\" authenticated", wp->username);
    } else {
        trace(5, "Password for user \"%s\" failed to authenticate", wp->username);
    }
    return success;
}

Json::Value WebHandler::UidWebResult(Webs *wp, const std::string &node)
{
    HTELINK_LOG_DEBUG("web handler, node: %s", node.c_str());
    std::string longest_prefix = UID_WEB_NODES.longest_prefix(node).key();
    HTELINK_LOG_DEBUG("longest prefix, key: %s", longest_prefix.c_str());
    std::function<Json::Value(Webs *, const std::string &)> func = UID_WEB_NODES.longest_prefix(node).value();
    if (!func) {
        return Json::nullValue;
    }
    const std::string subId = node.substr(longest_prefix.length());
    HTELINK_LOG_DEBUG("subId = %s", subId.c_str());
    return func(wp, subId);
}

Json::Value WebHandler::AjaxWebResult(Webs *wp, const std::string &node)
{
    HTELINK_LOG_DEBUG("web handler, node: %s", node.c_str());
    WebsNode::const_iterator webNode = AJAX_WEB_NODES.longest_prefix(node);
    if (webNode == AJAX_WEB_NODES.end()) {
        HTELINK_LOG_ERR("web handler, node: %s not found", node.c_str());
        return Json::nullValue;
    }
    std::string longestPrefix = webNode.key();
    HTELINK_LOG_DEBUG("longest prefix, key: %s", longestPrefix.c_str());
    std::function<Json::Value(Webs *, const std::string &)> func = webNode.value();
    if (!func) {
        return Json::nullValue;
    }
    const std::string subId = node.substr(std::min(node.length(), longestPrefix.length() + 1));
    HTELINK_LOG_DEBUG("subId = %s", subId.c_str());
    return func(wp, subId);
}

bool WebHandler::WebResult(Webs *wp, const std::string &prefix)
{
    HTELINK_LOG_DEBUG("webs ajax path: %s, prefix = %s, user = %s, method = %s, protocol = %s",
        wp->path == nullptr ? "" : wp->path, wp->route->prefix, wp->username, wp->method, wp->protocol);
    std::string path = wp->path;
#undef min
    path = path.substr(std::min(path.length(), prefix.length()));
    Json::Value result;
    if (prefix == "/ajax/") {
        result = AjaxWebResult(wp, path);
        websSetStatus(wp, 200);
    } else if (prefix == "/uid/") {
        result = UidWebResult(wp, path);
        websSetStatus(wp, 200);
    } else {
        websSetStatus(wp, 401);
    }

    websWriteHeaders(wp, -1, 0);
    websWriteEndHeaders(wp);

    Json::FastWriter jsonWriter;
    std::string strJson = jsonWriter.write(result);
    HTELINK_LOG_DEBUG("response: %s", result.toStyledString().c_str());
    websWrite(wp, strJson.c_str());
    websDone(wp);
    return true;
}
